/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <unistd.h>
#include "iservice_registry.h"
#include "i_sle_host.h"
#include "sle_ssap_server_impl.h"
#include "sle_common_config.h"
#include "sle_connect_config.h"
#include "sle_hilog.h"

namespace OHOS {
namespace NearLink {
namespace SleStandard {
SleSsapServer::SleSsapServerImpl::SleSsapServerImpl(const SleUuid &uuid) : serverId_(0)
{
    SLE_LOGI("SleSsapServerImpl In");
    Uuid sUuid(uuid);
    serverUuid_ = sUuid;

    callbackImpl_ = new SleSsapServerCallbackStubImpl(*this);
    SLE_ASSERT_AND_RETURN_LOG(callbackImpl_, "callbackImpl null error!");

    auto resetCallback = [this](const wptr<IRemoteObject> &remote) { this->ResetService(remote); };
    deathRecipient_ = new SleDeathRecipient(resetCallback);
    SLE_ASSERT_AND_RETURN_LOG(deathRecipient_, "deathRecipient null error!");

    std::lock_guard<std::mutex> lock(mutexProxy_);
    GetProxy();
    SLE_ASSERT_AND_RETURN_LOG(proxy_, "get proxy error!");
}

SleSsapServer::SleSsapServerImpl::~SleSsapServerImpl()
{
    SLE_LOGI("~SleSsapServerImpl In");
    SLE_ASSERT_AND_RETURN_LOG(proxy_, "ssap server proxy null error!");
    if (isRegistered_) {
        SleErrorCode ret = proxy_->UnregisterSsapServer(serverId_, callbackImpl_);
        SLE_ASSERT_AND_RETURN_LOG(ret == SLE_NO_ERROR, "unregister ssap server failed!");
    }
}

void SleSsapServer::SleSsapServerImpl::GetProxy()
{
    if (proxy_) {
        return;
    }
    sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    SLE_ASSERT_AND_RETURN_LOG(samgr, "no samgr error!");

    sptr<IRemoteObject> hostRemote = samgr->GetSystemAbility(KH_SLE_SERVICE_SYS_ABILITY_ID_);

    SLE_ASSERT_AND_RETURN_LOG(hostRemote, "no remote error!");
    sptr<ISleHost> hostProxy = iface_cast<ISleHost>(hostRemote);
    SLE_ASSERT_AND_RETURN_LOG(hostProxy, "host proxy null error!");

    sptr<IRemoteObject> remote = hostProxy->GetSleServerRemote(SLE_SSAP_SERVER_SERVER);
    SLE_ASSERT_AND_RETURN_LOG(remote, "no remote error!");

    SLE_ASSERT_AND_RETURN_LOG(deathRecipient_, "deathRecipient null error!");
    SLE_ASSERT_AND_RETURN_LOG(!((remote->IsProxyObject()) && (!remote->AddDeathRecipient(deathRecipient_))),
        "Failed to add death recipient");

    proxy_ = iface_cast<ISleSsapServer>(remote);
    SLE_ASSERT_AND_RETURN_LOG(proxy_, "no announce proxy error!");
    SleErrorCode ret = proxy_->RegisterSsapServer(serverUuid_, serverId_, callbackImpl_);
    SLE_ASSERT_AND_RETURN_LOG(ret == SLE_NO_ERROR, "register ssap server failed!");
    isRegistered_ = true;
    SLE_LOGI("GetProxy success");
}

void SleSsapServer::SleSsapServerImpl::ResetService(const wptr<IRemoteObject> &remote)
{
    SLE_ASSERT_AND_RETURN_LOG(remote != nullptr, "remote null error!");
    std::lock_guard<std::mutex> lock(mutexProxy_);
    SLE_ASSERT_AND_RETURN_LOG(proxy_, "no proxy error!");
    sptr<IRemoteObject> serviceRemote = proxy_->AsObject();
    if ((serviceRemote != nullptr) && (serviceRemote == remote.promote())) {
        serviceRemote->RemoveDeathRecipient(deathRecipient_);
        proxy_ = nullptr;
        int tryTime = 5;
        while ((!proxy_) && tryTime > 0) {
            sleep(GET_PROXY_SLEEP_SEC);
            tryTime--;
            GetProxy();
        }
    }
}

SleErrorCode SleSsapServer::SleSsapServerImpl::CheckOperation()
{
    std::lock_guard<std::mutex> lock(mutexProxy_);
    GetProxy();
    SLE_ASSERT_AND_RETURN_RET_LOG(proxy_, SLE_ERR_INTERNAL_WRONG, "proxy null error!");
    return SLE_NO_ERROR;
}

//SleSsapServer
SleSsapServer::SleSsapServer(const SleUuid &uuid)
{
    SLE_LOGI("SleSsapServer In");
    SLE_ASSERT_AND_RETURN_LOG(Uuid::CheckSleUuid(uuid) == SLE_NO_ERROR, "check server app uuid failed!");
    pimpl_ = std::make_unique<SleSsapServerImpl>(uuid);
    SLE_ASSERT_AND_RETURN_LOG(pimpl_, "pimpl null error!");
}

SleSsapServer::~SleSsapServer()
{
    SLE_LOGI("~SleSsapServer In");
}

SleErrorCode SleSsapServer::AddService(const SleUuid &uuid, bool isPrimary, uint16_t &handle)
{
    SLE_LOGD("AddService In");
    SLE_ASSERT_AND_RETURN_RET_LOG(Uuid::CheckSleUuid(uuid) == SLE_NO_ERROR,
       SLE_ERR_INTERNAL_WRONG, "param service uuid error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    Uuid srcUuid(uuid);
    Uuid serviceUuid = srcUuid;
    ret = pimpl_->proxy_->AddService(pimpl_->serverId_, serviceUuid, isPrimary, handle);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "add service failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapServer::AddProperty(uint16_t serviceHandle, const SsapsPropertyParam &param, uint16_t &handle)
{
    SLE_LOGD("AddProperty In");
    SLE_ASSERT_AND_RETURN_RET_LOG(serviceHandle != 0, SLE_ERR_INTERNAL_WRONG, "param service handle error!");
    SleErrorCode ret = SsapServerPropertyParam::CheckSsapsPropertyParam(param);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check param failed(%{public}d)!", ret);
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SsapServerPropertyParam propertyParam(param);
    ret = pimpl_->proxy_->AddProperty(pimpl_->serverId_, serviceHandle, propertyParam, handle);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "add property failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapServer::AddDescriptor(uint16_t serviceHandle, uint16_t propHandle, const SsapsDescriptorParam &param)
{
    SLE_LOGD("AddDescriptor In");
    SLE_ASSERT_AND_RETURN_RET_LOG(serviceHandle != 0, SLE_ERR_INTERNAL_WRONG, "param service handle error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(propHandle > 1, SLE_ERR_INTERNAL_WRONG, "param property handle error!");
    SleErrorCode ret = SsapServerDescriptorParam::CheckSsapsDescriptorParam(param);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check param failed(%{public}d)!", ret);
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SsapServerDescriptorParam descriptorParam(param);
    ret = pimpl_->proxy_->AddDescriptor(pimpl_->serverId_, serviceHandle, propHandle, descriptorParam);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "add descriptor failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapServer::RemoveAllServices()
{
    SLE_LOGD("RemoveAllServices In");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->proxy_->RemoveAllServices(pimpl_->serverId_);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "remove all services failed!");

    std::unique_lock<std::mutex> lock(pimpl_->mtx_);
    auto status = pimpl_->conditionVariable_.wait_for(
        lock, std::chrono::milliseconds(3000), [this] { return pimpl_->ready_; });
    pimpl_->ready_ = false;
    if (!status) {
        SLE_LOGE("timeout, remove all services failed!");
        return SLE_ERR_TIME_OUT;
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_->errCode_ == SLE_NO_ERROR, pimpl_->errCode_, "remove all services failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapServer::StartService(uint16_t serviceHandle)
{
    SLE_LOGD("StartService In");
    SLE_ASSERT_AND_RETURN_RET_LOG(serviceHandle != 0, SLE_ERR_INTERNAL_WRONG, "param service handle error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->proxy_->StartService(pimpl_->serverId_, serviceHandle);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "start service failed!");

    std::unique_lock<std::mutex> lock(pimpl_->mtx_);
    auto status = pimpl_->conditionVariable_.wait_for(
        lock, std::chrono::milliseconds(3000), [this] { return pimpl_->ready_; });
    pimpl_->ready_ = false;
    if (!status) {
        SLE_LOGE("timeout, start service failed!");
        return SLE_ERR_TIME_OUT;
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_->errCode_ == SLE_NO_ERROR, pimpl_->errCode_, "start service failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapServer::SendResponse(uint16_t connectId, const SsapsSendRspParam &param)
{
    SLE_LOGD("SendResponse In");
    SLE_ASSERT_AND_RETURN_RET_LOG(connectId < SLE_MAX_CONNECT_ID_NUM,
        SLE_ERR_INTERNAL_WRONG, "param connect id error!");
    SleErrorCode ret = SsapServerSendRspParam::CheckSsapsSendRspParam(param);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check param failed(%{public}d)!", ret);
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SsapServerSendRspParam params(param);
    ret = pimpl_->proxy_->SendResponse(pimpl_->serverId_, connectId, params);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "send response failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapServer::NotifyIndicate(uint16_t connectId, const SsapsNotifyParam &param)
{
#if 0
    SLE_LOGD("NotifyIndicate In");
#endif
    SLE_ASSERT_AND_RETURN_RET_LOG(connectId < SLE_MAX_CONNECT_ID_NUM,
        SLE_ERR_INTERNAL_WRONG, "param connect id error!");
    SleErrorCode ret = SsapServerNotifyParam::CheckSsapsNotifyParam(param);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check param failed(%{public}d)!", ret);
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SsapServerNotifyParam params(param);
    ret = pimpl_->proxy_->NotifyIndicate(pimpl_->serverId_, connectId, params);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "notify indicate failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapServer::NotifyIndicateByUuid(uint16_t connectId, const SsapsNotyfyByUuidParam &param)
{
    SLE_LOGD("NotifyIndicateByUuid In");
    SLE_ASSERT_AND_RETURN_RET_LOG(connectId < SLE_MAX_CONNECT_ID_NUM,
        SLE_ERR_INTERNAL_WRONG, "param connect id error!");
    SleErrorCode ret = SsapServerNotyfyByUuidParam::CheckSsapsNotyfyByUuidParam(param);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check param failed(%{public}d)!", ret);
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SsapServerNotyfyByUuidParam params(param);
    ret = pimpl_->proxy_->NotifyIndicateByUuid(pimpl_->serverId_, connectId, params);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "notify indicate by uuid failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapServer::SetServerMtuInfo(const SsapMtuInfo &info)
{
    SLE_LOGD("SetServerMtuInfo In");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->proxy_->SetServerMtuInfo(pimpl_->serverId_, info.mtuSize, info.version);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "set server mtu info failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapServer::RegisterServerCallback(std::shared_ptr<SleSsapServerCallback> callback)
{
    SLE_LOGD("RegisterServerCallback In");
    SLE_ASSERT_AND_RETURN_RET_LOG(callback, SLE_ERR_INVALID_PARAM, "param callback nullptr error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_->callbacks_.Register(callback), SLE_ERR_FAILED,
        "register server callback failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapServer::UnregisterServerCallback(std::shared_ptr<SleSsapServerCallback> callback)
{
    SLE_LOGD("UnregisterServerCallback In");
    SLE_ASSERT_AND_RETURN_RET_LOG(callback, SLE_ERR_INVALID_PARAM, "param callback nullptr error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_->callbacks_.Unregister(callback), SLE_ERR_FAILED,
        "unregister server callback failed!");
    return SLE_NO_ERROR;
}
} // SleStandard
} // NearLink
} // OHOS